package com.lqg.bookLibrary.service.recommend;

import com.lqg.bookLibrary.pojo.Borrow;
import com.lqg.bookLibrary.service.IBorrowService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class BorrowedHistoryRecommend<TB, TU> {

    @Autowired
    private IBorrowService borrowService;

    // 用户借阅历史数据
    // 每个用户的历史借阅数据，其中键为用户ID，值为用户购买的图书列表。
    //private Map<TB, List<TB>> purchaseHistory;

    /**
     * 计算用户购买历史中每对图书之间的相关度
     * calculateItemOccurrence函数计算每对图书之间的相关度，
     * 返回一个Map<TB, Map<TB, Integer>>类型的变量，
     * 其中键为一本书的名称，值为与该书有相关度的其他书籍及其相关度。
     */
    private Map<TB, Map<TB, Integer>> calculateItemOccurrence(Map<TU, List<TB>> purchaseHistory) {
        // 返回值 map
        // key 一本书
        // value->key：另一本书
        // value->value: 两本书的相关度
        Map<TB, Map<TB, Integer>> itemOccurrence = new HashMap<>();
        // 遍历所有的用户借阅历史
        for (List<TB> purchases : purchaseHistory.values()) {
            for (int i = 0; i < purchases.size(); i++) {
                // 一本图书
                TB item1 = purchases.get(i);
                // 如果结果集中没有key 为 item1 的 则new
                if (!itemOccurrence.containsKey(item1)) {
                    itemOccurrence.put(item1, new HashMap<>());
                }
                // 计算item1 与 该用户其他书本之间的 的 相似度 前面的已经计算过故 j从i+1开始
                for (int j = i + 1; j < purchases.size(); j++) {
                    TB item2 = purchases.get(j);
                    // 结果集中如果没有item1 与 item2 的 相关度 先置为0
                    if (!itemOccurrence.get(item1).containsKey(item2)) {
                        itemOccurrence.get(item1).put(item2, 0);
                    }
                    // 相似度计算  item1 与 item2 相似度
                    // 相似度 计算 出现 在原来的基础+1
                    itemOccurrence.get(item1).put(item2, itemOccurrence.get(item1).get(item2) + 1);
                }
            }
        }

        return itemOccurrence;
    }

    // 给用户推荐图书
    // recommendBooks函数接受一个用户ID，根据该用户的借阅历史和所有用户的借阅历史，
    // 计算推荐候选集，然后按相关度从高到低进行排序，并返回一个推荐的图书列表。
    public List<TB> recommendBooks(TU userId) {

        Map<TU, List<TB>> purchaseHistory = new HashMap<>();

        List<Borrow> list = borrowService.list();

        for (Borrow borrow : list) {
            if (purchaseHistory.containsKey(borrow.getUserId())) {
                purchaseHistory.get(borrow.getUserId()).add((TB) borrow.getBookId());
            }else {
                List<TB> tbArrayList = new ArrayList<>();
                tbArrayList.add((TB) borrow.getBookId());
                purchaseHistory.put((TU) borrow.getUserId(), tbArrayList);
            }
        }

        // 候选集
        Map<TB, Integer> candidateItems = new HashMap<>();

        Map<TU, List<TB>> purchaseHistoryWithoutUser = new HashMap<>(purchaseHistory);
        // 去除当前用户的购买历史
        purchaseHistoryWithoutUser.remove(userId);
        Map<TB, Map<TB, Integer>> itemOccurrence = calculateItemOccurrence(purchaseHistory); // 计算相关度

        // 遍历 去除 userid的 借阅历史记录
        for (List<TB> purchases : purchaseHistoryWithoutUser.values()) {
            // purchases 为另一位用户的购买历史
            // item 一本书
            for (TB item : purchases) {
                // 如果当前用户已经购买过这本书，则不推荐
                if (purchaseHistory.get(userId).contains(item)) {
                    continue;
                }

                // 遍历 userid 用户的 购买历史记录
                for (TB item2 : purchaseHistory.get(userId)) {
                    // 查看 item （另一位用户购买的一本书） 与 item2 （userid 用户购买的一本书） 之间的相似度
                    // 如果当前书籍与当前用户购买历史中的书籍没有相关度，则跳过
                    if (!itemOccurrence.containsKey(item) || !itemOccurrence.get(item).containsKey(item2)) {
                        continue;
                    }
                    // 相似度存在 put 到 待选列表
                    // key 为 item（另一位用户购买的一本书）
                    // value 已存在的 权值 加上 相似度权值
                    candidateItems.put(item,
                            candidateItems.getOrDefault(item, 0) + itemOccurrence.get(item).get(item2));
                }
            }
        }

        // 按相关度排序
        List<TB> sortedBooks = new ArrayList<>(candidateItems.keySet());
        sortedBooks.sort((item1, item2) -> candidateItems.get(item2) - candidateItems.get(item1));

        return sortedBooks;
    }

}
